#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#include "utils.h"
#include "mouses.h"

volatile struct mouseDataFrame mouse0Data, mouse1Data;
volatile uint8_t mouse0Buffer[3], mouse1Buffer[3];

extern volatile int16_t good0Bytes, bad0Bytes;
extern volatile int16_t good1Bytes, bad1Bytes;

ISR(INT0_vect) {
	//  if host is sending to mouse
	if(mouse0Data.info & MOUSE_DATA_INFO_MODE_SEND) {
		// if start bit hasn't beed sent yet
		if (! (mouse0Data.info & MOUSE_DATA_INFO_START_BIT_DONE)) {
			mouse0Data.info |= MOUSE_DATA_INFO_START_BIT_DONE;
			return;
		}
		//if hasn't sent 8 data bits yet
		else if (mouse0Data.bitsCounter < 8) {
			if ((mouse0Data.data >> mouse0Data.bitsCounter) & 1) {
				MOUSE_PORT |= MOUSE_0_DATA_PIN;
				mouse0Data.info ^= 1;
			}
			else {
				MOUSE_PORT &= ~ MOUSE_0_DATA_PIN;
			}
		}
		//if has sent 8 data bits already, send parity bit
		else if (8 == mouse0Data.bitsCounter) {
			if ((mouse0Data.info & 1) != 0) {
				MOUSE_PORT &= ~ MOUSE_0_DATA_PIN;
			}
			else {
				MOUSE_PORT |= MOUSE_0_DATA_PIN;
			}
		}
		//else put data line high and set it to input mode.
		else if (9 == mouse0Data.bitsCounter) {
			MOUSE_DDR &= ~ MOUSE_0_DATA_PIN;
			MOUSE_PORT |= MOUSE_0_DATA_PIN;
		}
		//ack came - send mouse state to recieve and not transmiting.
		else {
			mouse0Data.info = 0;
		}
		mouse0Data.bitsCounter++;
	}
	//if mouse is sending to host
	else {
		//if first clock signal - omit start bit
		if (! (mouse0Data.info & MOUSE_DATA_INFO_START_BIT_DONE)) {
			mouse0Data.bitsCounter = 0;
			mouse0Data.data = 0;
			mouse0Data.info |= MOUSE_DATA_INFO_IS_TRANSMITING | MOUSE_DATA_INFO_START_BIT_DONE;
		}
		//if hasn't sent 8 data bits yet
		else if (mouse0Data.bitsCounter < 8) {
			if (MOUSE_PIN & MOUSE_0_DATA_PIN) {
				mouse0Data.data |= (1 << mouse0Data.bitsCounter);
				mouse0Data.info ^= 1;
			}
			else {
				mouse0Data.data &= ~(1 << mouse0Data.bitsCounter);
			}
			mouse0Data.bitsCounter++;
		}
		// parity bit
		else if (8 == mouse0Data.bitsCounter) {
			if ( (((mouse0Data.info & 1) == 0) && ((MOUSE_PIN & MOUSE_0_DATA_PIN) != 0))
			|| (((mouse0Data.info & 1) != 0) && ((MOUSE_PIN & MOUSE_0_DATA_PIN) == 0)) ) {
				//parity ok
				good0Bytes++;
			}
			else {
				//parity failed
				bad0Bytes++;
			}
			mouse0Data.bitsCounter++;
		}
		// stop bit
		else {
			mouse0Data.packets[mouse0Data.frameNumber] = mouse0Data.data;
			if (mouse0Data.frameNumber==2) {
				mouse0Buffer[0] = mouse0Data.packets[0];
				mouse0Buffer[1] = mouse0Data.packets[1];
				mouse0Buffer[2] = mouse0Data.packets[2];
				mouse0Data.info = MOUSE_DATA_INFO_GOT_DATA;
			}
			else mouse0Data.info = 0;
			mouse0Data.frameNumber = (mouse0Data.frameNumber+1)%3;
		}
	}
}

ISR(INT1_vect) {
	//  if host is sending to mouse
	if(mouse1Data.info & MOUSE_DATA_INFO_MODE_SEND) {
		// if start bit hasn't beed sent yet
		if (! (mouse1Data.info & MOUSE_DATA_INFO_START_BIT_DONE)) {
			mouse1Data.info |= MOUSE_DATA_INFO_START_BIT_DONE;
			return;
		}
		//if hasn't sent 8 data bits yet
		else if (mouse1Data.bitsCounter < 8) {
			if ((mouse1Data.data >> mouse1Data.bitsCounter) & 1) {
				MOUSE_PORT |= MOUSE_1_DATA_PIN;
				mouse1Data.info ^= 1;
			}
			else {
				MOUSE_PORT &= ~ MOUSE_1_DATA_PIN;
			}
		}
		//if has sent 8 data bits already, send parity bit
		else if (8 == mouse1Data.bitsCounter) {
			if ((mouse1Data.info & 1) != 0) {
				MOUSE_PORT &= ~ MOUSE_1_DATA_PIN;
			}
			else {
				MOUSE_PORT |= MOUSE_1_DATA_PIN;
			}
		}
		//else put data line high and set it to input mode.
		else if (9 == mouse1Data.bitsCounter) {
			MOUSE_DDR &= ~ MOUSE_1_DATA_PIN;
			MOUSE_PORT |= MOUSE_1_DATA_PIN;
		}
		//ack came - send mouse state to recieve and not transmiting.
		else {
			mouse1Data.info = 0;
		}
		mouse1Data.bitsCounter++;
	}
	//if mouse is sending to host
	else {
		//if first clock signal - omit start bit
		if (! (mouse1Data.info & MOUSE_DATA_INFO_START_BIT_DONE)) {
			mouse1Data.bitsCounter = 0;
			mouse1Data.data = 0;
			mouse1Data.info |= MOUSE_DATA_INFO_IS_TRANSMITING | MOUSE_DATA_INFO_START_BIT_DONE;
		}
		//if hasn't sent 8 data bits yet
		else if (mouse1Data.bitsCounter < 8) {
			if (MOUSE_PIN & MOUSE_1_DATA_PIN) {
				mouse1Data.data |= (1 << mouse1Data.bitsCounter);
				mouse1Data.info ^= 1;
			}
			else {
				mouse1Data.data &= ~(1 << mouse1Data.bitsCounter);
			}
			mouse1Data.bitsCounter++;
		}
		// parity bit: 0 - odd, 1 - even
		else if (8 == mouse1Data.bitsCounter) {
			if ( (((mouse1Data.info & 1) == 0) && ((MOUSE_PIN & MOUSE_1_DATA_PIN) != 0))
			|| (((mouse1Data.info & 1) != 0) && ((MOUSE_PIN & MOUSE_1_DATA_PIN) == 0)) ) {
				//parity ok
				good1Bytes++;
			}
			else {
				//parity failed
				bad1Bytes++;
			}
			mouse1Data.bitsCounter++;
		}
		// stop bit
		else {
			mouse1Data.packets[mouse1Data.frameNumber] = mouse1Data.data;
			if (mouse1Data.frameNumber==2) {
				mouse1Buffer[0] = mouse1Data.packets[0];
				mouse1Buffer[1] = mouse1Data.packets[1];
				mouse1Buffer[2] = mouse1Data.packets[2];
				mouse1Data.info = MOUSE_DATA_INFO_GOT_DATA;
			}
			else mouse1Data.info = 0;
			mouse1Data.frameNumber = (mouse1Data.frameNumber+1)%3;
		}
	}
}

void sendByte(uint8_t byte) {

	// wait if there is any transmition to mouse
	while ((mouse0Data.info & MOUSE_DATA_INFO_IS_TRANSMITING) || (mouse1Data.info & MOUSE_DATA_INFO_IS_TRANSMITING));

	//disable INT0 and INT1 in global interrupt mask
	GIMSK &= ~(_BV(INT0) | _BV(INT1));

	// set clock line to output mode and put it low for more than 100ms
	MOUSE_PORT &= ~(MOUSE_0_CLOCK_PIN | MOUSE_1_CLOCK_PIN);
	MOUSE_DDR |= MOUSE_0_CLOCK_PIN | MOUSE_1_CLOCK_PIN;
	_delay_us(MOUSE_RTS_DELAY);

	// set data to send
	mouse0Data.data = byte;
	mouse0Data.bitsCounter = 0;
	mouse0Data.frameNumber = 0;
	mouse0Data.info |= MOUSE_DATA_INFO_MODE_SEND | MOUSE_DATA_INFO_IS_TRANSMITING;

	mouse1Data = mouse0Data;

	// set data line to output mode and put it low
	MOUSE_PORT &= ~(MOUSE_0_DATA_PIN | MOUSE_1_DATA_PIN);
	MOUSE_DDR |= MOUSE_0_DATA_PIN | MOUSE_1_DATA_PIN;

	//enable INTO and INT1 in global interrupt mask
	GIMSK |= _BV(INT0) | _BV(INT1);

	// put clock line high and set to input mode
	MOUSE_PORT |= MOUSE_0_CLOCK_PIN | MOUSE_1_CLOCK_PIN;
	MOUSE_DDR &= ~(MOUSE_0_CLOCK_PIN | MOUSE_1_CLOCK_PIN);

	//GIFR &= ~(_BV(INTF0) | _BV(INTF1));

	// data will be sent by interrupt handler. wait for transmition finish.
	while ((mouse0Data.info & MOUSE_DATA_INFO_IS_TRANSMITING) ||
		 (mouse1Data.info & MOUSE_DATA_INFO_IS_TRANSMITING));

}


inline void initMouses() {

	// set clock line to input mode and pull up
	MOUSE_PORT |= MOUSE_0_CLOCK_PIN | MOUSE_1_CLOCK_PIN;
	MOUSE_DDR &= ~(MOUSE_0_CLOCK_PIN | MOUSE_1_CLOCK_PIN);

	// set data line to input mode and pull up
	MOUSE_PORT |= MOUSE_0_DATA_PIN | MOUSE_1_DATA_PIN;
	MOUSE_DDR &= ~(MOUSE_0_DATA_PIN | MOUSE_1_DATA_PIN);

	//set INT0 and INT1 to falling edge trigger
	MCUCR |= _BV(ISC01) | _BV(ISC11);
	MCUCR &= ~(_BV(ISC00) | _BV(ISC10));

	// enable INT0 and INT1 interrupts 
	GIMSK |= _BV(INT0) | _BV(INT1);

	// send reset comand and wait for ack, self-test passed and mouse id
	sendByte(0xFF);
	_delay_ms(500);

	// send set resolution comand and wait for ack
	sendByte(0xE8);
	_delay_ms(30);
	  
		
	// set resolution to 1/mm and wait for ack
	sendByte(0x00);
	_delay_ms(30);


	// send enable comand and wait for ack
	sendByte(0xF4);
	_delay_ms(30);


	mouse0Data.info = 0;
	mouse1Data.info = 0;

	mouse0Data.frameNumber = 0;
	mouse1Data.frameNumber = 0;

} 

uint8_t getMouse0Data(uint8_t data[]) {
	if (mouse0Data.info & MOUSE_DATA_INFO_GOT_DATA) {
		data[0] = mouse0Buffer[0];
		data[1] = mouse0Buffer[1];
		data[2] = mouse0Buffer[2];
		mouse0Data.info &= ~ MOUSE_DATA_INFO_GOT_DATA;
		return 1;
	}
	else return 0;
}

uint8_t getMouse1Data(uint8_t data[]) {
	if (mouse1Data.info & MOUSE_DATA_INFO_GOT_DATA) {
		data[0] = mouse1Buffer[0];
		data[1] = mouse1Buffer[1];
		data[2] = mouse1Buffer[2];
		mouse1Data.info &= ~ MOUSE_DATA_INFO_GOT_DATA;
		return 1;
	}
	else return 0;
}

